package com.lebang.service;

import com.alibaba.fastjson.JSONObject;
import com.lebang.common.Xml;
import com.lebang.common.util.SortUtil;
import com.lebang.common.utils.OkHttpUtil;
import com.lebang.model.WeixinInfo;
import com.lebang.model.enums.Gender;
import com.lebangweb.common.UUIDUtil;
import com.lebangweb.encrypt.MessageDigestUtil;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.net.ssl.SSLContext;
import java.io.File;
import java.io.FileInputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.KeyStore;
import java.util.HashMap;
import java.util.Map;

/**
 * 微信支付Service
 *
 * @author Eliohn on 2019/9/12.
 * @version 1.0
 */
@Service
public class WxPayService {
    @Value("${info.url}")
    private String baseUrl;
    @Value("${wx.wxcertpath}")
    private String wxCertPath;
    @Value("${wx.mchid}")
    private String MCH_ID;
    @Value("${wx.key}")
    private String API_KEY;
    @Value("${wx.mch_appid}")
    private String MCH_APPID;
    //秘钥
    @Value("${wx.oauth.secert}")
    private  String SECERT ;
    @Value("${wx.oauth.app_id}")
    private String APP_ID ;


    private static final String WX_URL = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers";

    private static final String REDIRECT_URI = "/weixin/notifly";
    private static final String OAUTH2_URL_TEMPLATE = "https://open.weixin.qq.com/connect/oauth2/authorize?appid={appid}&redirect_uri={redirect_uri}&response_type=code&scope=snsapi_userinfo&state={memberId}#wechat_redirect";
    //微信请求获取openid地址
    private static final String CATCH_OPENID_URL = "https://api.weixin.qq.com/sns/oauth2/access_token?appid={appid}&secret={secret}&code={code}&grant_type=authorization_code";
    public static final String USERINFO_URL = "https://api.weixin.qq.com/sns/userinfo?access_token={access_token}&openid={openid}&lang=zh_CN";

    /**
     * 通过用户的　ID　取授权的地址
     *
     * @param memberId
     * @return
     */
    public String getAuthUrl(String memberId) {
        try {
            return OAUTH2_URL_TEMPLATE.replace("{memberId}", memberId).replace("{redirect_uri}", URLEncoder.encode(baseUrl+REDIRECT_URI, "utf-8")).replace("{appid}", APP_ID);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     *
     * 自动微信提现
     *
     * @param outTradeNo 订单号
     * @param total      金额 分
     * @param desc       备注
     * @param ip         用户 ip
     * @param openid     用户 openid
     * @return
     */
    public Map<String, String> weChatTransfer(String outTradeNo, String total,
                                              String desc, String ip, String openid, String name) {
        String noncestr = UUIDUtil.M();
        HashMap<String, String> parameters = new HashMap<String, String>();
        //  必填参数
        parameters.put("mch_appid", MCH_APPID);                //微信支付分配的公众账号ID（企业号corpid即为此appId）
        parameters.put("mchid", MCH_ID);
        parameters.put("nonce_str", noncestr);                //随机字符串，长度要求在32位以内。
        parameters.put("check_name", "FORCE_CHECK");                        //校验用户姓名选项开启
        parameters.put("partner_trade_no", outTradeNo);            //商户系统内部订单号，要求32个字符内，只能是数字、大小写字母_-|* 且在同一个商户号下唯一
        parameters.put("amount", total);//订单总金额，单位为分
        parameters.put("desc", desc);        //备注
        parameters.put("spbill_create_ip", ip);            //用户ip
        parameters.put("openid", openid);                    //用户openid
        parameters.put("re_user_name", name); //真实姓名
        // 加入签名值
        parameters.put("sign", makeSign(parameters));                    //通过签名算法计算得出的签名值    必填参数
        String send = "";
        try {
            send = postXML(WX_URL, parameters);
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (send == null || "".equals(send)) {
            return null;
        }
        return Xml.parse(send);
    }
    /**
     * 微信的签名
     *
     * @param param 上报的参数
     * @return 签名结果
     */
    private String makeSign(Map<String, String> param) {

        Map<String, String> paramAsc = SortUtil.sortMapByKeyAsc(param);

        StringBuilder str = new StringBuilder();
        for (String key : paramAsc.keySet()) {
            if (!paramAsc.get(key).isEmpty()) {
                str.append(key).append("=").append(paramAsc.get(key)).append("&");
            }
        }
        if (str.length() > 0) {
            str = new StringBuilder(str.substring(0, str.length() - 1));
        }

        String stringSignTemp = str + "&key=" + API_KEY;
        String sign = null;
        try {
            sign = MessageDigestUtil.encryptMD5(stringSignTemp.getBytes(StandardCharsets.UTF_8)).toUpperCase();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return sign;

    }

    /**
     * post 请求
     *
     * @author wzx
     * @time 2017年9月19日 上午10:05:45
     */
    private String postXML(String url, HashMap<String, String> params) {
        String result = null;
        HttpPost httpPost = new HttpPost(url);
        String postDataXML = Xml.format(params);
        // 得指明使用UTF-8编码，否则到API服务器XML的中文不能被成功识别
        StringEntity postEntity = new StringEntity(postDataXML, StandardCharsets.UTF_8);
        httpPost.setHeader("Accept", "application/json");
        httpPost.setHeader("Content-type", "application/json");
        httpPost.setEntity(postEntity);
        // 设置请求器的配置
        try {

            HttpClient httpClient = wrapClient(url);
            HttpResponse response = httpClient.execute(httpPost);
            HttpEntity entity = response.getEntity();
            result = EntityUtils.toString(entity, "UTF-8");
        } catch (Exception e) {
            e.printStackTrace();

        } finally {
            httpPost.abort();
        }
        return result;
    }


    /**
     * 获取 HttpClient
     *
     * @param url
     * @return
     */
    private HttpClient wrapClient(String url) throws Exception {
        KeyStore keyStore = KeyStore.getInstance("PKCS12");

        FileInputStream instream = new FileInputStream(new File(wxCertPath));

        keyStore.load(instream, MCH_ID.toCharArray());

        instream.close();

        SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, MCH_ID.toCharArray()).build();

//        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[]{"TLSv1"}, null,
//                SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext);
        return HttpClients.custom().setSSLSocketFactory(sslsf).build();
    }


    /**
     *  获取用户的微信信息
     * @param code 扫码返回的CODE
     * @return　用户信息
     */
    public WeixinInfo getWeixinUserInfo(String code) {
        JSONObject jsonObject = OkHttpUtil.getRequest(CATCH_OPENID_URL.replace("{appid}", APP_ID).replace("{secret}", SECERT).replace("{code}", code));
        String error = jsonObject.getString("errcode");
        if (error == null) {
            String openid = jsonObject.getString("openid");
            String accessToken = jsonObject.getString("access_token");
            jsonObject = OkHttpUtil.getRequest(USERINFO_URL.replace("{access_token}", accessToken).replace("{openid}", openid));
            error = jsonObject.getString("errcode");
            if (error == null) {
                String nickname = jsonObject.getString("nickname");
                String sex;
                if (jsonObject.getInteger("sex") == 1) {
                    sex = Gender.Male.name();
                } else {
                    sex = Gender.Female.name();
                }
                String headimgurl = jsonObject.getString("headimgurl");
                WeixinInfo weixinInfo = new WeixinInfo();
                weixinInfo.setOpenId(openid);
                weixinInfo.setStatus(1);
                weixinInfo.setNickName(nickname);
                weixinInfo.setAvatar(headimgurl);
                weixinInfo.setGender(sex);
                return weixinInfo;
            }
        }
        return null;
    }
}